#include <syslog.h>
#include <netinet/ether.h>
#include "scps.h"
#include "scps_ip.h"
#include "scpsudp.h"
#include "tp_debug.h"  // Included for LOG_PACKET in case DEBUG_TCPTRACE is defined.
#include "tap.h"
#include "other_proto_handler.h"
#include "rs_config.h"

extern struct msghdr out_msg;
extern struct llbuf *in_data;

extern int scps_udp_port;
extern int errno;

udp_Header *up;


extern GW_ifs gw_ifs;

extern void err_dump ();

int tap_open (char *dev)
{
  struct ifreq ifr;
  int fd, err;
  if ((fd = open("/dev/net/tun", O_RDWR)) < 0){
      return fd;
  }
  memset(&ifr, 0, sizeof(ifr));

  ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
  if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
      close(fd);
      return err;
  }

  if (fcntl (fd, F_SETFL, O_NDELAY) > 0)
    err_dump ("fcntl problem");

  if (fcntl (fd, F_SETOWN, getpid ()) > 0)
    err_dump ("fcntl problem with F_SETOWN");

  if(dev){
    strncpy(dev, ifr.ifr_name,IFNAMSIZ);
  }

   int opts = fcntl(fd, F_GETFL);
   opts |= O_NONBLOCK;
   fcntl(fd, F_SETFL, opts);
  return fd;
}

int ll_tap_qk_send  (int tun_fd, unsigned char *data, int len)
{
  int rc;
  rc = write (tun_fd, data, len);
  LOG ("Gateway: tun(%d) write rc=%d\n", tun_fd, rc); 
  if (rc < 0) {
    rc = 0;
  }
  return (rc);
}


int ll_tap_send  (struct gwio*gwio, uint32_t remote_internet_addr,
            int protocol, int data_len, struct msghdr *my_msg,
            route *a_route, scps_np_rqts *rqts)
{
  int sock = 0;
  struct sockaddr_in remote_addr;
  unsigned char linear_buffer[MAX_MTU];
  int length;
  int size = 0;
  int rval;
  int i;
  memset((char *) &remote_addr, 0, sizeof(remote_addr));
  remote_addr.sin_family = AF_INET;
  remote_addr.sin_port = htons(scps_udp_port);
  remote_addr.sin_addr.s_addr=remote_internet_addr;
  my_msg->msg_iov[0].iov_len = 0;

  sock = rqts->recv_tap_if; 

  my_msg->msg_name = (caddr_t) &remote_addr;
  my_msg->msg_namelen = sizeof(remote_addr);

    // Toss this and replace write with a writev().

  memcpy (&(linear_buffer [0]), &(rqts->dst_mac_addr [0]), MAC_ADDR_SIZE);
  memcpy (&(linear_buffer [MAC_ADDR_SIZE]), &(rqts->src_mac_addr [0]), MAC_ADDR_SIZE);
  linear_buffer [START_OF_FRAME_TYPE] = (rqts->frame_type & (0xff00)) >> 8; 
  linear_buffer [START_OF_FRAME_TYPE +1] = (rqts->frame_type & (0x00ff));

  for (i = 0,length=0,size=SIZE_OF_ETHER_PART; i < my_msg->msg_iovlen; i++) {
    length = my_msg->msg_iov[i].iov_len;
    memcpy(&linear_buffer[size], my_msg->msg_iov[i].iov_base, length);
    size += length;
  }

  rval=write(sock,linear_buffer,size);
  return rval;

}

int tap_ind (struct gwio *gwio, struct llbuf **buffer, int max_len, int *offset, scps_np_rqts *rqts)
{
  int interface_side = -1;
  int fd = -1;
  struct ethhdr *ethhdr = (struct ethhdr *)((*buffer)->data + (*buffer)->offset);
  memcpy (rqts->dst_mac_addr, ethhdr->h_dest, ETH_ALEN);
  memcpy (rqts->src_mac_addr, ethhdr->h_source, ETH_ALEN);

  rqts->frame_type = ntohs(ethhdr->h_proto);

  if ((*buffer)->divert_port_number == gwio->div_a_port) {
    rqts->recv_tap_if = gwio->tap_a_fd;
    rqts->peer_tap_if = gwio->tap_b_fd;
    rqts->divert_port_number = gw_ifs.a.divport;
    interface_side = AIF;
    fd = gwio->tap_b_fd;
  } else if ((*buffer)->divert_port_number == gwio->div_b_port) {
    rqts->recv_tap_if = gwio->tap_b_fd;
    rqts->peer_tap_if = gwio->tap_a_fd;
    rqts->divert_port_number = gw_ifs.b.divport;
    interface_side = BIF;
    fd = gwio->tap_a_fd;
  }
  LOG("in_data frame_type 0x%x\n", in_data->frame_type);
  switch (in_data->frame_type) {
  case 0x0806:
    LOG("buffer dport %d a_dport %d b_dport %d\n", (*buffer)->divert_port_number, gwio->div_a_port, gwio->div_b_port);

    if ((*buffer)->divert_port_number == gwio->div_a_port) {
      fd = gwio->tap_b_fd;
    } else if ((*buffer)->divert_port_number == gwio->div_b_port) {
      fd = gwio->tap_a_fd; 
    } else {
      LOG ("ERROR in TAP_ind bad divert_port_number\n");
      free_llbuf (rqts->gwio, *buffer);
      return (0);
    }
    ll_tap_qk_send (fd, ((*buffer)->data +(*buffer)->offset), (*buffer)->size); 
    free_llbuf(rqts->gwio, *buffer);
    return (0);
  case 0x86dd:

/* Need to check for the IPv6 equivalent of ARP for IPv6
 * (Router Solicitation and Router Advertisement */
    (*buffer)->offset += ETH_HLEN;
    (*buffer)->size -= ETH_HLEN;
    *offset += ETH_HLEN;

    if ((int)*((*buffer)->data + (*buffer)->offset + 6) == 0x3a) { /* ICMP */
      if ( ((unsigned char )*((*buffer)->data + (*buffer)->offset + 40) == 135) ||  /* 135 */
        ((unsigned char )*((*buffer)->data + (*buffer)->offset + 40) == 136) )   { /* 136 */
        int fd;

        if ((*buffer)->divert_port_number == gwio->div_a_port) {
          fd = gwio->tap_b_fd;
        } else if ((*buffer)->divert_port_number == gwio->div_b_port) {
            fd = gwio->tap_a_fd;
        } else {
          LOG ("ERROR in TAP_ind bad divert_port_number\n");
          return (0);
        }

        (*buffer)->offset -=ETH_HLEN;
        (*buffer)->size +=ETH_HLEN;
        *offset -=ETH_HLEN;

        ll_tap_qk_send (fd, ((*buffer)->data +(*buffer)->offset), (*buffer)->size);
        free_llbuf (rqts->gwio, *buffer);
        return (0);

      }
    }
    return ((*buffer)->size);
  case 0x0800:
    (*buffer)->offset +=ETH_HLEN;
    (*buffer)->size -=ETH_HLEN;
    *offset +=ETH_HLEN;
    return ((*buffer)->size);
  break;

  default:
    if (gw_ifs.c_other_proto_non_ip == 1) {
      other_proto_non_ip_Handler (interface_side, buffer, rqts, fd);
      return (0);
    }
    free_llbuf (rqts->gwio, *buffer);
    return (0);
  break;
  }
  return (0);
}


